﻿/*
 * File: ThreeDeeGamePlayBaseScene.cs
 * Author: Gourav Das
 * Purpose: Draw Scene for 3D gameplay
 * Created: June 17 2011
 */

using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Threading;
using System.IO;
using _22yards._2D;
using _22yards.Scenes.Helpers;
using _22yards.Core;
using _22yards;
using _22yards.EnumDeclarations;
using _22yards.ThreeD;
using _22yards.Helpers;
using Komires.MataliPhysics;

namespace _22yards.Scenes
{
	/// <summary>
	/// This is a scene that implements the 2D Gameplay screen
	/// </summary>
	partial class GamePlayBaseScene : Scene
	{
		private void InitializeBase3DScene()
		{
			PhysxEngine = new PhysicsEngine("22Yards PhysxEngine");
			PhysxScene = PhysxEngine.Factory.PhysicsSceneManager.Create("22Yards PhysxScene");
			Gravity = new Vector3(0, -0.2f, 0);
			PhysxScene.SetGravityDirection(ref Gravity);
		}

		private void Draw3DScene()
		{
			if (ShowShadows)
			{
				ShadowMapTexture = GetShadowTexture(broadcastBallChaseCam);
				LeftStumpFarEnd.ShadowMapTexture = ShadowMapTexture;
				RightStumpFarEnd.ShadowMapTexture = ShadowMapTexture;
				CenterStumpFarEnd.ShadowMapTexture = ShadowMapTexture;
				LeftStumpNearEnd.ShadowMapTexture = ShadowMapTexture;
				RightStumpNearEnd.ShadowMapTexture = ShadowMapTexture;
				CenterStumpNearEnd.ShadowMapTexture = ShadowMapTexture;
				LeftBailFarEnd.ShadowMapTexture = ShadowMapTexture;
				RightBailFarEnd.ShadowMapTexture = ShadowMapTexture;
				LeftBailNearEnd.ShadowMapTexture = ShadowMapTexture;
				RightBailNearEnd.ShadowMapTexture = ShadowMapTexture;
				KBRedBall.ShadowMapTexture = ShadowMapTexture;
				stadia.ShadowMapTexture = ShadowMapTexture;
				Pitch.ShadowMapTexture = ShadowMapTexture;
				LeftStumpFarEnd.Technique = "NormalMappingWithShadow";
				RightStumpFarEnd.Technique = "NormalMappingWithShadow";
				CenterStumpFarEnd.Technique = "NormalMappingWithShadow";

				LeftStumpNearEnd.Technique = "NormalMappingWithShadow";
				RightStumpNearEnd.Technique = "NormalMappingWithShadow";
				CenterStumpNearEnd.Technique = "NormalMappingWithShadow";

				LeftBailFarEnd.Technique = "NormalMappingWithShadow";
				RightBailFarEnd.Technique = "NormalMappingWithShadow";
				LeftBailNearEnd.Technique = "NormalMappingWithShadow";
				RightBailNearEnd.Technique = "NormalMappingWithShadow";
				KBRedBall.Technique = "NormalMappingWithShadow";
				stadia.Technique = "NormalMappingWithShadow";
				Pitch.Technique = "NormalMappingWithShadow";
			}
			else
			{
				LeftStumpFarEnd.Technique = "NormalMappingNoShadow";
				RightStumpFarEnd.Technique = "NormalMappingNoShadow";
				CenterStumpFarEnd.Technique = "NormalMappingNoShadow";

				LeftStumpNearEnd.Technique = "NormalMappingNoShadow";
				RightStumpNearEnd.Technique = "NormalMappingNoShadow";
				CenterStumpNearEnd.Technique = "NormalMappingNoShadow";

				LeftBailFarEnd.Technique = "NormalMappingNoShadow";
				RightBailFarEnd.Technique = "NormalMappingNoShadow";
				LeftBailNearEnd.Technique = "NormalMappingNoShadow";
				RightBailNearEnd.Technique = "NormalMappingNoShadow";
				KBRedBall.Technique = "NormalMappingNoShadow";
				stadia.Technique = "NormalMappingNoShadow";

				if (ReliefMapping)
				{
					//Pitch.SetModelMeshHeightMapTextureMaterial("pitch", TexPitchHeight);
					//Pitch.SetModelMeshNormalMapTextureMaterial("pitch", TexPitchNormal);
					//Pitch.HeightScale += 0.01f;
					Pitch.Technique = "POMMappingNoShadow";
				}
				else
				{
					//Pitch.SetModelMeshNormalMapTextureMaterial("pitch", TexPitchNormal);
					Pitch.Technique = "NormalMappingNoShadow";
				}
			}

			sceneEngine.GraphicsDevice.SetRenderTarget(RenderTarget3DScene);
			RenderTarget3DScene.GraphicsDevice.Clear(Color.DarkSlateBlue);

			LeftStumpFarEnd.Draw(broadcastBallChaseCam, Matrix.Identity);    
			RightStumpFarEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			CenterStumpFarEnd.Draw(broadcastBallChaseCam, Matrix.Identity);

			LeftStumpNearEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			RightStumpNearEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			CenterStumpNearEnd.Draw(broadcastBallChaseCam, Matrix.Identity);

			LeftBailFarEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			RightBailFarEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			LeftBailNearEnd.Draw(broadcastBallChaseCam, Matrix.Identity);
			RightBailNearEnd.Draw(broadcastBallChaseCam, Matrix.Identity);

			if (KBRedBall != null)
			{
				KBRedBall.Draw(broadcastBallChaseCam, Matrix.Identity);
			}

			stadia.Draw(broadcastBallChaseCam, Matrix.Identity);
			//Fielder1.Draw(broadcastBallChaseCam, Matrix.Identity);

			Pitch.Draw(broadcastBallChaseCam, Matrix.Identity);

			sceneEngine.GraphicsDevice.SetRenderTarget(null);
			Original3DTexture = RenderTarget3DScene;

			if (ShowBloom)
			{
				////////////////////////////////////////
				// Render the bright areas of the scene in SceneTexture to a new texture
				sceneEngine.GraphicsDevice.SetRenderTarget(RenderTargetBloom);
				RenderTargetBloom.GraphicsDevice.Clear(Color.Black);

				sceneEngine.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
				{
					// Apply the post process shader
					PostEffect.CurrentTechnique = PostEffect.Techniques["Bloom"];
					PostEffect.CurrentTechnique.Passes[0].Apply();
					{
						sceneEngine.SpriteBatch.Draw(Original3DTexture, new Vector2(0, 0), Color.White);
					}
				}
				sceneEngine.SpriteBatch.End();

				sceneEngine.GraphicsDevice.SetRenderTarget(null);
				BloomTexture = RenderTargetBloom;

				////////////////////////////////////////
				// Blur the bright areas in the BloomTexture, making them "glow"
				sceneEngine.GraphicsDevice.SetRenderTarget(RenderTargetBlurBloom);
				sceneEngine.GraphicsDevice.Clear(Color.Black);
				sceneEngine.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
				{
					// Apply the post process shader
					if (ShowGaussianBlur)
					{
						PostEffect.CurrentTechnique = PostEffect.Techniques["GaussianBlur"];
					}
					else
						PostEffect.CurrentTechnique = PostEffect.Techniques["Blur"];
					
					PostEffect.Parameters["weights"].SetValue(weights);
					PostEffect.Parameters["offsets"].SetValue(offsetsHoriz);
					PostEffect.CurrentTechnique.Passes[0].Apply();
					{
						sceneEngine.SpriteBatch.Draw(BloomTexture, new Vector2(0, 0), Color.White);
					}
				}
				sceneEngine.SpriteBatch.End();
				sceneEngine.GraphicsDevice.SetRenderTarget(null);
				BlurBloomTexture = RenderTargetBlurBloom;

				////////////////////////////////////////
				// Blur the bright areas in the BloomTexture a 2nd time, making them "glow" even more
				sceneEngine.GraphicsDevice.SetRenderTarget(RenderTargetBlurIIBloom);
				sceneEngine.GraphicsDevice.Clear(Color.Black);
				sceneEngine.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
				{
					// Apply the post process shader
					if (ShowGaussianBlur)
					{
						PostEffect.CurrentTechnique = PostEffect.Techniques["GaussianBlur"];
					}
					else
						PostEffect.CurrentTechnique = PostEffect.Techniques["Blur"];

					PostEffect.Parameters["weights"].SetValue(weights);
					PostEffect.Parameters["offsets"].SetValue(offsetsHoriz);
					PostEffect.CurrentTechnique.Passes[0].Apply();
					{
						sceneEngine.SpriteBatch.Draw(BlurBloomTexture, new Vector2(0, 0), Color.White);
					}
				}
				sceneEngine.SpriteBatch.End();
				sceneEngine.GraphicsDevice.SetRenderTarget(null);
				BlurIIBloomTexture = RenderTargetBlurIIBloom;

				sceneEngine.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.DarkSlateBlue, 1.0f, 0);

				sceneEngine.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
				{
					// Apply the post process shader
					PostEffect.CurrentTechnique = PostEffect.Techniques["BloomCombine"];

					PostEffect.CurrentTechnique.Passes[0].Apply();
					{
						PostEffect.Parameters["ColorTexture"].SetValue(Original3DTexture);
						sceneEngine.SpriteBatch.Draw(BlurIIBloomTexture, new Vector2(0, 0), Color.White);
						if(ShowShadowMap && ShadowMapTexture != null)
							sceneEngine.SpriteBatch.Draw(ShadowMapTexture, new Rectangle(1366 - 256, 768 - 256, 256, 256), Color.White);
					}
				}
				sceneEngine.SpriteBatch.End();
			}
			else
			{
				sceneEngine.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
					sceneEngine.SpriteBatch.Draw(Original3DTexture, new Vector2(0, 0), Color.White);
					if(ShowShadowMap && ShadowMapTexture != null)
						sceneEngine.SpriteBatch.Draw(ShadowMapTexture, new Rectangle(1366 - 256, 768 - 256, 256, 256), Color.White);
				sceneEngine.SpriteBatch.End();
			}
		}

		private Texture2D GetShadowTexture(BaseCamera camera)
		{
			sceneEngine.GraphicsDevice.SetRenderTarget(ShadowMapTarget);
			ShadowMapTarget.GraphicsDevice.Clear(Color.White);

			Matrix lightsViewProjectionMatrix = CreateLightViewProjectionMatrix(camera);

			LeftStumpFarEnd.Technique = "ShadowMap";
			LeftStumpFarEnd.lightsViewProjection = lightsViewProjectionMatrix;
			LeftStumpFarEnd.Draw(null, Matrix.Identity);
			RightStumpFarEnd.Technique = "ShadowMap";
			RightStumpFarEnd.lightsViewProjection = lightsViewProjectionMatrix;
			RightStumpFarEnd.Draw(null, Matrix.Identity);
			CenterStumpFarEnd.Technique = "ShadowMap";
			CenterStumpFarEnd.lightsViewProjection = lightsViewProjectionMatrix;
			CenterStumpFarEnd.Draw(null, Matrix.Identity);

			LeftStumpNearEnd.Technique = "ShadowMap";
			LeftStumpNearEnd.lightsViewProjection = lightsViewProjectionMatrix;
			LeftStumpNearEnd.Draw(null, Matrix.Identity);
			RightStumpNearEnd.Technique = "ShadowMap";
			RightStumpNearEnd.lightsViewProjection = lightsViewProjectionMatrix;
			RightStumpNearEnd.Draw(null, Matrix.Identity);
			CenterStumpNearEnd.Technique = "ShadowMap";
			CenterStumpNearEnd.lightsViewProjection = lightsViewProjectionMatrix;
			CenterStumpNearEnd.Draw(null, Matrix.Identity);


			LeftBailFarEnd.Technique = "ShadowMap";
			LeftBailFarEnd.lightsViewProjection = lightsViewProjectionMatrix;
			LeftBailFarEnd.Draw(null, Matrix.Identity);
			RightBailFarEnd.Technique = "ShadowMap";
			RightBailFarEnd.lightsViewProjection = lightsViewProjectionMatrix;
			RightBailFarEnd.Draw(null, Matrix.Identity);
			LeftBailNearEnd.Technique = "ShadowMap";
			LeftBailNearEnd.lightsViewProjection = lightsViewProjectionMatrix;
			LeftBailNearEnd.Draw(null, Matrix.Identity);
			RightBailNearEnd.Technique = "ShadowMap";
			RightBailNearEnd.lightsViewProjection = lightsViewProjectionMatrix;
			RightBailNearEnd.Draw(null, Matrix.Identity);

			if (KBRedBall != null)
			{
				KBRedBall.Technique = "ShadowMap";
				KBRedBall.lightsViewProjection = lightsViewProjectionMatrix;
				KBRedBall.Draw(null, Matrix.Identity);
			}

			stadia.Technique = "ShadowMap";
			stadia.lightsViewProjection = lightsViewProjectionMatrix;
			stadia.Draw(null, Matrix.Identity);
			//Fielder1.Draw(broadcastBallChaseCam, Matrix.Identity);

			Pitch.Technique = "ShadowMap";
			Pitch.lightsViewProjection = lightsViewProjectionMatrix;
			Pitch.Draw(null, Matrix.Identity);

			sceneEngine.GraphicsDevice.SetRenderTarget(null);
			//ShadowMapTexture = ShadowMapTarget;

			return ShadowMapTarget;
		}

		Matrix CreateLightViewProjectionMatrix(BaseCamera camera)
		{
			// 1. Calculate the world space location of the 8 corners of the
			// view frustum.

			BoundingFrustum frustum = new BoundingFrustum(camera.ViewMatrix * camera.ProjectionMatrix);
			Vector3[] frustumCornersWorldSpace = frustum.GetCorners();

			// 2. Calculate the centroid of the frustum.

			Vector3 centroid = Vector3.Zero;

			foreach (Vector3 frustumCorner in frustumCornersWorldSpace)
				centroid += frustumCorner;

			centroid /= (float)frustumCornersWorldSpace.Length;

			// 3. Calculate the position of the direction light.
			// Start at the centroid and then move back in the opposite
			// direction of the light by an amount equal to the camera's far
			// clip plane. This is the position of the light.

			float distance = Math.Abs(frustum.Near.D) + Math.Abs(frustum.Far.D);
			Matrix lightView = Matrix.CreateLookAt(centroid - (SunlightDir * distance), centroid, Vector3.Up);

			// 4. Calculate the light space locations of the 8 corners of the
			// (world space) view frustum. The lightViewMatrix is used to
			// transform each world space frustum corner into light space.

			Vector3[] frustumCornersLightSpace = new Vector3[frustumCornersWorldSpace.Length];
			Vector3.Transform(frustumCornersWorldSpace, ref lightView, frustumCornersLightSpace);

			// 5. Calculate the bounding box for the light space frustum
			// corners. The bounding box is used to construct the proper
			// orthographic projection matrix for the directional light.

			BoundingBox box = BoundingBox.CreateFromPoints(frustumCornersLightSpace);
			Matrix lightProjection = Matrix.CreateOrthographicOffCenter(
				box.Min.X, box.Max.X, box.Min.Y, box.Max.Y, -box.Max.Z, -box.Min.Z);

			//camera.viewMatrix = lightView;
			//camera.position = lightPosition;
			//camera.projectionMatrix = lightProjection;

			return lightView * lightProjection;

			//// Matrix with that will rotate in points the direction of the light
			//Matrix lightRotation = Matrix.CreateLookAt(Vector3.Zero,
			//                                           -SunlightDir,
			//                                           Vector3.Up);

			//// Get the corners of the frustum
			//BoundingFrustum cameraFrustum = new BoundingFrustum(camera.ViewMatrix * camera.ProjectionMatrix);
			////Vector3[] frustumCornersWorldSpace = frustum.GetCorners();
			//Vector3[] frustumCorners = cameraFrustum.GetCorners();

			//// Transform the positions of the corners into the direction of the light
			//for (int i = 0; i < frustumCorners.Length; i++)
			//{
			//    frustumCorners[i] += new Vector3(1f, 1f, 1f);
			//    frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightRotation);
			//}

			//// Find the smallest box around the points
			//BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);

			//Vector3 boxSize = lightBox.Max - lightBox.Min;
			//Vector3 halfBoxSize = boxSize * 0.5f;

			//// The position of the light should be in the center of the back
			//// pannel of the box. 
			//Vector3 lightPosition = lightBox.Min + halfBoxSize;
			//lightPosition.Z = lightBox.Min.Z;

			//// We need the position back in world coordinates so we transform 
			//// the light position by the inverse of the lights rotation
			//lightPosition = Vector3.Transform(lightPosition,
			//                                  Matrix.Invert(lightRotation));

			//// Create the view matrix for the light
			//Matrix lightView = Matrix.CreateLookAt(lightPosition,
			//                                       lightPosition - SunlightDir,
			//                                       Vector3.Up);

			//// Create the projection matrix for the light
			//// The projection is orthographic since we are using a directional light
			//Matrix lightProjection = Matrix.CreateOrthographic(boxSize.X, boxSize.Y,
			//                                                   -boxSize.Z - 20f, boxSize.Z);

			//return lightView * lightProjection;
		}
	}

}